自动化脚本优化:导出所有 Composition API(Hooks)
概述
组件库的全局注册解决了组件模板使用的问题,但 useForm、useMenu 等 Composition API(Hooks)没有从入口文件导出,导致 Playground 中无法使用。本节修改自动化脚本,将所有 use* 开头的 Hooks 自动导出。
问题分析
缺失的导出
// 自动生成的入口文件(缺少 Hooks 导出)
export { default as VpForm } from './form/index.vue'
export { default as VpTable } from './table/index.vue'
export { default as VpMenu } from './menu/index.vue'
// 缺少以下导出:
// export { useForm } from './form/useForm'
// export { useMenu } from './menu/useMenu'
// export { useTable } from './table/useTable'
typescript
期望的使用方式
// 在使用组件库的项目中
import { VpForm, useForm } from '@el-admin/components'
const { register, validate } = useForm()
typescript
脚本修改
修改前(仅导出组件)
// scripts/gen-components.ts
components.forEach((filePath) => {
const { baseName, ext } = path.parse(filePath)
exports += `export { default as ${baseName} } from '${filePath}'\n`
})
typescript
修改后(同时导出 Hooks)
// scripts/gen-components.ts
let componentExports = ''
let hooksExports = ''
function generateExports(components: string[], hooks: string[]) {
// 导出 Vue 组件
for (const filePath of components) {
const { baseName } = path.parse(filePath)
componentExports += `export { default as ${baseName} } from './${filePath}'\n`
}
// 导出 Composition API(use* 文件)
for (const filePath of hooks) {
const { baseName, ext } = path.parse(filePath)
if (ext === '.ts' && baseName.startsWith('use')) {
hooksExports += `export { ${baseName} } from './${filePath}'\n`
}
}
return componentExports + '\n' + hooksExports
}
typescript
扫描 Hooks 文件
// 遍历组件目录,识别所有 use*.ts 文件
function scanHooks(componentDir: string): string[] {
const hooks: string[] = []
function walk(dir: string) {
const files = fs.readdirSync(dir)
for (const file of files) {
const fullPath = path.join(dir, file)
const stat = fs.statSync(fullPath)
if (stat.isDirectory()) {
walk(fullPath)
} else if (file.startsWith('use') && file.endsWith('.ts')) {
hooks.push(fullPath)
}
}
}
walk(componentDir)
return hooks
}
typescript
生成的入口文件
// src/main.ts(自动生成)
// 组件导出
export { default as VpForm } from './form/index.vue'
export { default as VpTable } from './table/index.vue'
export { default as VpMenu } from './menu/index.vue'
// Hooks 导出
export { useForm } from './form/useForm'
export { useTable } from './table/useTable'
export { useMenu } from './menu/useMenu'
// 全局注册
export default {
install(app: App) {
app.component('VpForm', VpForm)
app.component('VpTable', VpTable)
app.component('VpMenu', VpMenu)
},
}
typescript
执行与调试
# 执行自动化脚本
pnpm run build:script:cgs
# 常见错误排查
# Error: "startsWith is not a function"
# → 检查 baseName 是否正确解析,可能遗漏了 .startsWith 的 s
# 验证导出
cat src/main.ts | grep "export { use"
bash
调试技巧
// 在脚本中添加调试日志
for (const filePath of files) {
const { baseName, ext } = path.parse(filePath)
console.log(`Scanning: ${baseName} (${ext})`) // 调试输出
if (ext === '.ts' && baseName.startsWith('use')) {
console.log(` → Hook found: ${baseName}`)
hooksExports += `export { ${baseName} } from './${filePath}'\n`
}
}
typescript
文件识别规则
| 文件名 | 匹配规则 | 处理方式 |
|---|---|---|
index.vue | 组件入口 | 全局注册 + 导出 |
useForm.ts | startsWith('use') + .ts | 仅导出 |
types.ts | 不匹配 | 不处理 |
constants.ts | 不匹配 | 不处理 |
实践要点
startsWith方法注意拼写(少写s是常见错误)- Hooks 文件的命名必须以
use开头,否则不会被自动导出 - 导出的 Hooks 需确保有正确的 TypeScript 类型声明
- 脚本执行后检查
main.ts的末尾,确认 Hooks 导出列表正确 - Playground 中使用时,从组件库入口统一导入:
import { useForm } from '@el-admin/components'
↑